using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using PHSRAG.Insight21.COI;
using PHSRAG.Utility;
using PHSRAG.Insight21.Policy;
using PHSRAG.Insight21.CDS;
using log4net;
using System.Net.Mail;
using System.Data.SqlClient;
using System.Collections;

namespace COIBatch
{
    public partial class COIBatch : Form
    {
        private const string COIAdminConfigKey = "Email.COI.Admin";
        private const string COIAdminToEmailConfigKey = "Email.COI.Admin.ToEmail";
        private static readonly ILog log = LogManager.GetLogger(typeof(COIBatch));
        private static int MaxEmailsPerRelay;

        public COIBatch()
        {
            InitializeComponent();
        }

        private void COIBatch_Load(object sender, EventArgs e)
        {
            log4net.Config.XmlConfigurator.Configure();
            log.Info("Application started");

            MaxEmailsPerRelay = Settings.GetKeyValue("MaxEmailsPerRelay", 100);

            // Setup open file dialog
            openFileDialog1.InitialDirectory = System.Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
            openFileDialog1.Filter = "Word Documents (*.doc, *.docx)|*.doc;*.docx|Excel Spreadsheets (*.xls, *.xlsx)|*.xls;*.xlsx;|PDF Documents (*.pdf)|*.pdf";
            openFileDialog1.FilterIndex = 1;

            // Populate Institutions
            DataTable instituionTable = COIUserSummary.GetInstitutionOrUnitOrDepartment(DataAccess.ConnectionString, 0, 0);
            DataRow dr = instituionTable.NewRow();
            dr["InstitutionId"] = 1;
            dr["Name"] = "<Partners>";
            instituionTable.Rows.InsertAt(dr, 0);
            lstInstitutions.DataSource = instituionTable;
            lstInstitutions.DisplayMember = "Name";
            lstInstitutions.ValueMember = "InstitutionId";
            lstInstitutions.SelectionMode = SelectionMode.MultiExtended;
            lstInstitutions.SelectedIndex = -1;

            // Populate Notification Types
            RefCodes refCodes = new RefCodes(new NullCachePolicy());
            refCodes.Load(DataAccess.ConnectionString, null);
            ArrayList notificationTypesRefCodes = refCodes.GetSortedRefCodes(Constants.NotificationTypes.Domain, Constants.NotificationTypes.AttributeOrder);
            notificationTypesRefCodes.RemoveRange(notificationTypesRefCodes.Count - 2, 2);
            cmbNotificationType.DataSource = notificationTypesRefCodes;
            cmbNotificationType.DisplayMember = "Meaning";
            cmbNotificationType.ValueMember = "Code";
            cmbNotificationType_SelectedIndexChanged(this, EventArgs.Empty);
        }

        private void btnAddCOIs_Click(object sender, EventArgs e)
        {
            try
            {
                if (MessageBox.Show("Are you sure you want to create new COIs?", "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) != DialogResult.Yes)
                    return;
                btnAddCOIs.Enabled = false;
                DateTime lastActivityDate = DateTime.Parse(txtLastActivity.Text);

                foreach (DataRowView item in lstInstitutions.SelectedItems)
                {
                    log.Info(string.Format("Creating COIs for {0} COI users and queuing notification emails", item["Name"]));
                    // Populate COIBatchPerson and get the list back.
                    // @@@ call COIUserSummary.ExecuteNotification
                    DataSet ds = DataAccess.GetDataSet(
                        DataAccess.ConnectionString, "dbo.GetCOIUsersForNotification", DataAccess.BuildSqlParameterArray(
                        "@type", SqlDbType.VarChar, cmbNotificationType.SelectedText
                        , "@institutionId", SqlDbType.Int, Convert.ToInt32(item["InstitutionId"])
                        , "@lastActivityDate", SqlDbType.DateTime, (lastActivityDate == DateTime.MinValue) ? (object)DBNull.Value : lastActivityDate
                        , "@reportingPeriodId", SqlDbType.Int, 2));

                    if (ds != null && ds.Tables.Count > 0)
                    {
                        ds.Tables[0].TableName = "ValidEmailList";
                        if (ds.Tables.Count == 2)
                            ds.Tables[1].TableName = "InvalidEmailList";
                    }

                    if (ds.Tables.Count == 2)
                    {
                        foreach (DataRow dr in ds.Tables["InvalidEmaillist"].Rows)
                        {
                            if (Convert.ToBoolean(dr[(int)NotifyUser.AddCOI]))
                            {
                                /// Call COICollection.AddCOI for each row
                                COICollection.AddNewCOI(new NullCachePolicy(), Convert.ToInt32(dr[(int)NotifyUser.AdmPersonId]), Constants.COIDateType.None);
                                // Set the COIAddedDate
                                DataAccess.ExecuteNonQuery(DataAccess.ConnectionString, "UpdateCOIBatchPerson",
                                    DataAccess.BuildSqlParameterArray(
                                    "@admPersonId", SqlDbType.Int, Convert.ToInt32(dr[(int)NotifyUser.AdmPersonId])
                                    , "@coiAddedDate", SqlDbType.DateTime, DateTime.Now
                                    ));
                            }
                        }
                    }
                    foreach (DataRow dr in ds.Tables["ValidEmailList"].Rows)
                    {
                        if (Convert.ToBoolean(dr[(int)NotifyUser.AddCOI]))
                        {
                            /// Call COICollection.AddCOI for each row
                            COICollection.AddNewCOI(new NullCachePolicy(), Convert.ToInt32(dr[(int)NotifyUser.AdmPersonId]), Constants.COIDateType.None);
                            // Set the COIAddedDate
                            DataAccess.ExecuteNonQuery(DataAccess.ConnectionString, "UpdateCOIBatchPerson",
                                DataAccess.BuildSqlParameterArray(
                                "@admPersonId", SqlDbType.Int, Convert.ToInt32(dr[(int)NotifyUser.AdmPersonId])
                                , "@coiAddedDate", SqlDbType.DateTime, DateTime.Now
                                ));
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                log.Error(string.Format("Exception in AddCOIs method :: {0}", ex.Message), ex);
                MessageBox.Show("Error in AddCOIs");
            }
            finally
            {
                btnAddCOIs.Enabled = true;
            }
        }

        private void btnSendEmails_Click(object sender, EventArgs e)
        {
            try
            {
                if (MessageBox.Show("Are you sure you want to send emails?", "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) != DialogResult.Yes)
                    return;
                btnSendEmails.Enabled = false;

                if (cmbNotificationType.SelectedValue.ToString() == "COINTFNTYPE_OPNENRLMNT")
                {
                    log.Info("Sending open enrollment emails");
                    ProcessCOIEmails(false);
                }
                else
                {
                    // If not open enrollment, we need to populate COIBatchPerson with the
                    // people to send reminders to
                    foreach (DataRowView item in lstInstitutions.SelectedItems)
                    {
                        log.Info(string.Format("Adding {0} COI users for {1} emails", item["Name"], cmbNotificationType.SelectedText));
                        // Populate COIBatchPerson and get the list back.
                        // @@@ call COIUserSummary.ExecuteNotification
                        DataSet ds = DataAccess.GetDataSet(
                            DataAccess.ConnectionString, "dbo.GetCOIUsersForNotification", DataAccess.BuildSqlParameterArray(
                            "@type", SqlDbType.VarChar, cmbNotificationType.SelectedValue
                            , "@institutionId", SqlDbType.Int, Convert.ToInt32(item["InstitutionId"])
                            , "@lastActivityDate", SqlDbType.DateTime, DBNull.Value
                            , "@reportingPeriodId", SqlDbType.Int, 0));
                    }
                    log.Info("Sending reminder emails");
                    ProcessCOIEmails(true);
                }
            }
            catch (Exception ex)
            {
                log.Error(string.Format("Exception in SendEmails method :: {0}", ex.Message), ex);
                MessageBox.Show("Error in SendEmails");
            }
            finally
            {
                btnSendEmails.Enabled = true;
            }
        }

        private void ProcessCOIEmails(bool reminders)
        {
            StringBuilder emailList = new StringBuilder();
            DataSet ds = DataAccess.GetDataSet(
                DataAccess.ConnectionString, "dbo.GetCOIBatchPeople", 
                DataAccess.BuildSqlParameterArray(
                "@reminders", SqlDbType.Bit, reminders));

            int i = 0;
            // create batch
            int batchId = Convert.ToInt32(DataAccess.ExecuteScalar(
                DataAccess.ConnectionString, "dbo.AddCOIBatch", null));

            foreach (DataRow dr in ds.Tables[0].Rows)
            {
                // set batch id on the people
                dr[(int)NotifyUser.COIBatchId] = batchId;

                emailList.Append(string.Format("{1}{0}",
                    dr[(int)NotifyUser.EmailAddress].ToString().TrimEnd('\r'),
                    emailList.Length == 0 ? "" : ", "));

                if ((i + 1) % MaxEmailsPerRelay == 0)
                {
                    // Save the batch ID on the COIBatchPerson rows
                    UpdateCOIBatchPeople(ds.Tables[0]);

                    SendMail(emailList.ToString(), batchId);
                    if (chkTestEmail.Checked)
                        return;

                    emailList = new StringBuilder(string.Empty);
                    batchId = Convert.ToInt32(DataAccess.ExecuteScalar(
                        DataAccess.ConnectionString, "dbo.AddCOIBatch", null));
                }
                ++i;
            }
            if (emailList.ToString() != string.Empty)
            {
                // Save the batch ID on the COIBatchPerson rows
                UpdateCOIBatchPeople(ds.Tables[0]);

                SendMail(emailList.ToString(), batchId);
                if (chkTestEmail.Checked)
                    return;
            }
        }

        private void UpdateCOIBatchPeople(DataTable table)
        {
            // save the batch ids
            SqlConnection conn = new SqlConnection(DataAccess.ConnectionString);
            conn.Open();
            SqlCommand command = new SqlCommand();
            command.CommandText = "UpdateCOIBatchPerson";
            command.CommandType = CommandType.StoredProcedure;
            command.Parameters.Add("@admPersonId", SqlDbType.Int).SourceColumn = "AdmPersonId";
            command.Parameters.Add("@coiAddedDate", SqlDbType.DateTime).SourceColumn = "COIAddedDate";
            command.Parameters.Add("@coiBatchId", SqlDbType.Int).SourceColumn = "COIBatchId";
            command.Connection = conn;

            SqlDataAdapter adapter = new SqlDataAdapter();
            adapter.UpdateCommand = command;
            adapter.Update(table);
            conn.Close();
        }

        private void SendMail(string emailList, int batchId)
        {
            MailMessage message = new MailMessage();
            try
            {
                if (string.IsNullOrEmpty(emailList))
                    throw new ArgumentNullException("emailList cannnot be null or empty.");

                message.From = new MailAddress(Settings.GetKeyValue(COIAdminConfigKey, string.Empty));
                message.To.Add((MaxEmailsPerRelay == 1) ? emailList : Settings.GetKeyValue(COIAdminToEmailConfigKey, string.Empty));
                if (MaxEmailsPerRelay != 1)
                    message.Bcc.Add(emailList);
                if (chkTestEmail.Checked)
                {
                    message.Bcc.Clear();
                    message.Bcc.Add(Settings.GetKeyValue("TestEmailAddress", "coiadmin@partners.org"));
                }
                message.Subject = txtSubject.Text.Trim();
                message.Body = emailBody.Text.Trim();
                message.IsBodyHtml = true;

                if (!string.IsNullOrEmpty(txtFile1.Text))
                    message.Attachments.Add(new Attachment(txtFile1.Text));
                if (!string.IsNullOrEmpty(txtFile2.Text))
                    message.Attachments.Add(new Attachment(txtFile2.Text));
                if (!string.IsNullOrEmpty(txtFile3.Text))
                    message.Attachments.Add(new Attachment(txtFile3.Text));

                SmtpClient client = new SmtpClient(Settings.GetKeyValue("Email.SMTPServer", "127.0.0.1"));
                client.UseDefaultCredentials = true;
                client.Send(message);

                // Set the sent date on the COIBatch
                DataAccess.ExecuteNonQuery(DataAccess.ConnectionString, "UpdateCOIBatch",
                    DataAccess.BuildSqlParameterArray(
                    "@coiBatchId", SqlDbType.Int, batchId
                    , "@emailSentDate", SqlDbType.DateTime, DateTime.Now
                    ));
            }
            catch (SmtpFailedRecipientsException ex)
            {
                for (int i = 0; i < ex.InnerExceptions.Length; i++)
                {
                    log.Error(
                        string.Format("Exception in SendMail method :: {0} :: Email address: {1}.",
                            ex.InnerExceptions[i].Message,
                            ex.InnerExceptions[i].FailedRecipient),
                        ex.InnerExceptions[i]);
                }
                // Set the sent date and exception text on the COIBatch
                DataAccess.ExecuteNonQuery(DataAccess.ConnectionString, "UpdateCOIBatch",
                    DataAccess.BuildSqlParameterArray(
                    "@coiBatchId", SqlDbType.Int, batchId
                    , "@emailSentDate", SqlDbType.DateTime, DateTime.Now
                    , "@exceptionText", SqlDbType.VarChar, ex.Message
                    ));
            }
            catch (SmtpFailedRecipientException ex)
            {
                log.Error(
                    string.Format("Exception in SendMail method :: {0} :: Email address: {1}.",
                        ex.Message,
                        ex.FailedRecipient),
                    ex);
                // Set the sent date and exception text on the COIBatch
                DataAccess.ExecuteNonQuery(DataAccess.ConnectionString, "UpdateCOIBatch",
                    DataAccess.BuildSqlParameterArray(
                    "@coiBatchId", SqlDbType.Int, batchId
                    , "@emailSentDate", SqlDbType.DateTime, DateTime.Now
                    , "@exceptionText", SqlDbType.VarChar, ex.Message
                    ));
            }
            catch (Exception ex)
            {
                log.Error(string.Format("Exception in SendMail method :: {0}", ex.Message), ex);
                // Set the exception text on the COIBatch
                DataAccess.ExecuteNonQuery(DataAccess.ConnectionString, "UpdateCOIBatch",
                    DataAccess.BuildSqlParameterArray(
                    "@coiBatchId", SqlDbType.Int, batchId
                    , "@emailSentDate", SqlDbType.DateTime, DBNull.Value
                    , "@exceptionText", SqlDbType.VarChar, ex.Message
                    ));
            }
            finally
            {
                message.Dispose();
            }
        }

        #region Event Handlers
        private void btnFile1_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                txtFile1.Text = openFileDialog1.FileName;
            }
        }

        private void btnFile2_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                txtFile2.Text = openFileDialog1.FileName;
            }
        }

        private void btnFile3_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                txtFile3.Text = openFileDialog1.FileName;
            }
        }

        private void lstInstitutions_Click(object sender, EventArgs e)
        {
            txtLastActivity.Enabled = btnAddCOIs.Enabled = (lstInstitutions.SelectedIndex >= 0 && cmbNotificationType.SelectedValue.ToString() == "COINTFNTYPE_OPNENRLMNT");
        }

        private void cmbNotificationType_SelectedIndexChanged(object sender, EventArgs e)
        {
            txtLastActivity.Enabled = btnAddCOIs.Enabled = 
                (lstInstitutions.SelectedIndex >= 0 && cmbNotificationType.SelectedValue.ToString() == "COINTFNTYPE_OPNENRLMNT");
            COIEMailTemplate mailTemplate = new COIEMailTemplate(new NullCachePolicy());
            mailTemplate.Load(DataAccess.ConnectionString, cmbNotificationType.SelectedValue.ToString());
            emailBody.Text = mailTemplate.BodyText;
            txtSubject.Text = mailTemplate.Subject;
        }
        #endregion

        private enum NotifyUser
        {
            EmailAddress = 0,
            FullName,
            AdmPersonId,
            AddCOI,
            COIAddedDate,
            COIBatchId
        }

    }
}